package tksTcp

import (
	"bytes"
	"encoding/binary"
	"net"
)

type ClientInit func(conn net.Conn)
type ClientReceive func(conn net.Conn, cmd uint32, from uint32, to uint32, data []byte)
type ClientClose func(conn net.Conn)

type TksTcpConn struct {
	Conn net.Conn
}

func (t *TksTcpConn) CreateServer(laddr string, init ClientInit, recv ClientReceive, closeClient ClientClose) error {
	listener, err := net.Listen("tcp", laddr)
	if err != nil {
		return err
	}
	for {
		t.Conn, err = listener.Accept()
		if err != nil {
			return err
		}
		init(t.Conn)
		//
		go ReceiveBuffer(t.Conn, recv, closeClient)
	}
}

func (t *TksTcpConn) CreateClient(laddr string, init ClientInit, recv ClientReceive, closeClient ClientClose) error {
	conn, err := net.Dial("tcp", laddr)
	t.Conn = conn
	if err != nil {
		return err
	}
	//
	go ReceiveBuffer(t.Conn, recv, closeClient)
	return nil
}

func SendBuffer(conn net.Conn, cmd uint32, from uint32, to uint32, buffer []byte) {
	if conn == nil {
		return
	}
	buf := make([]byte, len(buffer)+4+4+4+4)
	binary.BigEndian.PutUint32(buf[:4], uint32(len(buffer)))
	binary.BigEndian.PutUint32(buf[4:8], cmd)
	binary.BigEndian.PutUint32(buf[8:12], from)
	binary.BigEndian.PutUint32(buf[12:16], to)
	copy(buf[16:], buffer)
	conn.Write(buf)
}

// 前八个字节为长度+cmd命令
func ReceiveBuffer(conn net.Conn, recv ClientReceive, closeClient ClientClose) {
	cache := make([]byte, 1024)
	buf := bytes.NewBuffer(make([]byte, 0, 1024))
	var contentLen uint32
	var cmd uint32
	var from uint32
	var to uint32
	for {
		size, err := conn.Read(cache)
		if err != nil {
			closeClient(conn)
			return
		}
		buf.Write(cache[:size])
		for {
			if buf.Len() < 16 {
				break
			}
			if contentLen == 0 {
				len := make([]byte, 4)
				_, _ = buf.Read(len)
				contentLen = binary.BigEndian.Uint32(len)
				_, _ = buf.Read(len)
				cmd = binary.BigEndian.Uint32(len)
				_, _ = buf.Read(len)
				from = binary.BigEndian.Uint32(len)
				_, _ = buf.Read(len)
				to = binary.BigEndian.Uint32(len)
			}
			if int(contentLen) > buf.Len() || contentLen == 0 {
				break
			}
			data := make([]byte, contentLen)
			_, err = buf.Read(data)

			recv(conn, cmd, from, to, data)
			contentLen = 0
		}
	}
}
